Oracle APEX 26.1
APEXlang I Tried It, and Here's What Actually Changed for Me
When Oracle announced APEXlang in APEX 26.1, I wasn't sure what to make of it. Another export format? I've been building APEX apps for years and felt comfortable with the old ways. But after spending a few weeks actually using it on a real project, I have to say this one is different.
I'll be honest: I came in with low expectations. I've seen "improved export formats" before and they rarely changed my day-to-day much. So I ran APEXlang through its paces on an internal HR portal our team had been maintaining for about two years 14 pages, approval workflows, email triggers, a couple of interactive grids. Enough moving parts to surface real issues. Here's what I actually found.
Okay, so what even is APEXlang?
The short version: APEXlang is an open, declarative specification language for Oracle APEX apps. Instead of your application living inside one massive SQL dump, it's broken out into clean, readable files you can actually open in VS Code and understand at a glance.
I like to think of it as giving your APEX application a proper source code home the kind developers working in React or Java have always had. Each page, each shared component, each SQL script lives in its own structured file. Together they form an APEXlang package, which is a ZIP archive that looks like this when you unzip it:
- .apxApplication and page definition files. Each page gets its own file. On our HR portal this meant 14 separate files one per page which made it immediately obvious who had last touched what.
- .sqlNative SQL scripts. Supporting objects, install and deinstall scripts all sit right here alongside the application definition.
- .css / .jsCustom styling and JavaScript bundled with the project. No more hunting through the app builder for that one bit of custom CSS you added six months ago.
- staticImages, icons, logos static resources stored alongside everything else.
- sharedAuthentication schemes, authorization rules, LOVs, lists, breadcrumbs each type in its own file. Really useful when more than one person is touching shared components.
Once this package lives in Git, it becomes the governed, diffable source of truth for the entire application. That's something I'd wanted for years.
The headaches it actually fixes
There were real problems with the old approach that I'd quietly accepted as "just how APEX works." APEXlang addresses most of them. Here's what actually changed:
Pull requests that meant nothing
Reviewing a 12,000-line SQL export in a PR was theatre, not review. A colleague once approved a change that accidentally dropped a validation neither of us caught it in the diff. With APEXlang, changes to Page 5 show up in one file, not buried in a monolithic export.
AI tools were flying blind
I'd tried pasting bits of our SQL export into AI assistants before and the suggestions rarely made sense too much noise, not enough structure. With APEXlang, I pasted a single page file and got back an actually relevant suggestion about a redundant validation. First time that's happened.
Security audits were painful
Our security team had previously asked us to document every authorization check in the app manually. With APEXlang, those rules live in a readable shared file. The last audit went noticeably faster they could scan it themselves.
CI/CD was held together with tape
Our pipeline had a brittle import step that occasionally failed with a cryptic ORA error and no useful message. The SQLcl integration gives it something real to validate before anything touches the workspace. That step hasn't broken since we switched.
The actual step-by-step workflow
This is the bit I wish I'd had when I started a clear picture of the full loop from Builder to local editor and back. I'll walk through exactly how I set it up and where I stumbled, so you don't have to.
-
1
Export your app in APEXlang format from Builder
In APEX Builder, go to Export / Import → Export and select APEXlang as the format. You'll get a ZIP with all the structured files. The export type you pick here matters, so it's worth thinking about upfront:
Export Type When to use it What's included Standard Source control, day-to-day dev Developer comments and audit trail. No runtime data. Runtime Deploying to test or production Strips comments, locks build status to run-only. Full Moving an app across environments with data Runtime data + full audit info. Don't commit this to Git. Custom When you need exact control Flashback option, granular toggles for every export setting. First time I did this, I accidentally picked Full and committed a 47 MB ZIP to our repo. My team was not thrilled. Standard is almost always the right choice for Git. -
2
Open the project locally in VS Code
Unzip the export and open the folder in VS Code. You'll see your pages directory, shared components, SQL scripts, and static files all laid out cleanly. Even before installing any extension, you can read the
.apxfiles and make basic edits the syntax is readable enough that it doesn't feel foreign.Tip: On first open, VS Code may prompt you to install recommended extensions for the workspace. Click Install All it'll save you a step in the next stage. -
3
Install the Oracle SQL Developer Extension for VS Code
Search for "Oracle SQL Developer" in the Extensions panel (Ctrl+Shift+X) and install it. This is what unlocks syntax highlighting, Ctrl+Space code completion on APEXlang properties, and live error indicators that flag problems before you try to import.
The Problems panel (Ctrl+Shift+M) becomes your early-warning system it tells you which file and line has an issue. I caught two property-name typos on our first export before the database was even involved.
The extension requires VS Code 1.85 or later. I was on an older version and got a confusing activation error worth checking before you start. -
4
Create a database connection in VS Code
Open the Oracle SQL Developer panel from the sidebar, click Add Connection, and fill in your host, port, service name, and credentials. Once connected, you can browse schema objects, run queries, and import your APEXlang package directly from VS Code no switching back to Builder needed.
You can set up multiple connections (dev, test, prod) and switch between them as needed. I keep our dev connection as the default and only switch to test when I'm actually ready to deploy something.
Using Oracle Autonomous Database? You'll need a wallet-based connection the standard username/password flow won't work with ADB without the wallet file configured first. -
5
Modify your app using the structured .apx files
Open the page file you want to change say
pages/p00005-leave-requests.apxand edit it directly. The structure is readable enough that you get up to speed on a file quickly. Here's roughly what a form page looks like:pages/p00005-leave-requests.apx// Page 5 – Leave Request Submission page( name: Leave Request alias: leave-request region( name: request-form type: Form source { sql: ``` SELECT request_id, emp_id, leave_type, start_date, end_date, status FROM hr_leave_requests WHERE emp_id = :APP_USER_ID ``` } layout { position: BODY startNewRow: true } ) )
Before committing, run a validate to catch anything the editor missed:
$ apex validate -input ./leave-portalIt checks the whole package without touching the workspace. I run this before every commit it's caught a few mistakes that would have caused a failed import.
One thing that tripped me up: component names get auto-assigned system Static IDs on first export from an older app. APEX derives them from component names, so a region called "Leave Requests" becomesleave-requestsbut some of mine came out less readable than that. Worth reviewing them before your first edit session. -
6
Import the updated app back into your workspace
Either right-click the project folder in VS Code and choose Import to APEX, or run it from the terminal:
$ apex import -input ./leave-portalAPEX compiles the APEXlang source and deploys it. If anything goes wrong, the Problems panel tells you exactly which file and which line no guessing, no vague ORA errors to Google. Once I had the flow down, the full round-trip (export → edit → validate → import) took about four minutes.
For CI/CD: This sameapex importcommand works in a pipeline. We wired it into our GitHub Actions workflow. Much more reliable than our old import step, which had a habit of failing silently.
A look at the APEXlang syntax it's simpler than it looks
When I first opened a .apx file I wasn't sure what I was looking at. But after about twenty minutes I had the pattern down. There are really only a handful of constructs to learn:
| Construct | What it means |
|---|---|
| ( ) | Wraps a component a page, a region, or an item |
| { } | A property group like source, layout, or appearance |
| camelCase: | Property name followed by its value after the colon |
| ```...``` | Multi-line SQL, PL/SQL, or JavaScript just like markdown code fences |
| @name | A reference to another component for example, pointing to a parent region |
| [ ] | A list of multiple values |
The format reads almost like structured prose once you're in it. You can glance at a page file and know immediately what that page does compare that to hunting through a 10,000-line SQL export for the same information.
store-products but some of mine came out cryptic enough that I spent time figuring out what they referred to. A cleanup session upfront saves confusion later.
Three ways to work with it
One thing I appreciate is that Oracle hasn't locked APEXlang into a single workflow. Depending on what I'm doing, I reach for one of three tools:
APEX Builder
The familiar starting point. Export from the dialog, import by dragging the ZIP in. There's also a new read-only APEXlang view inside Page Designer useful for a quick sanity check before editing locally.
VS Code + SQL Developer
My daily driver now. Code completion, syntax highlighting, live validation, DB connection, and import all in one place. The Problems panel alone has saved me from several broken imports.
SQLcl CLI
For anything automated CI/CD, scripting, nightly deployments. Four clean commands cover everything I need. Wired into our pipeline and solid so far.
The four SQLcl commands worth knowing
The generate command is new in 26.1. I used it to scaffold a quick proof-of-concept last week you run the command, get a starter project on disk, and import it straight into your workspace without opening Builder. It works, though the scaffolded app is fairly bare-bones. Treat it as a skeleton rather than anything with structure built in.
deployments/default.json file won't have an application ID yet APEX assigns one on first import. If you need a specific ID, add it to that file before importing. I found this out the hard way when our test environment got a different ID than dev and a few hardcoded links broke.
My honest verdict
I went into APEXlang with low expectations and came out genuinely convinced it's the right direction. It's not just a new file format it's a proper rethink of how an Oracle APEX application can live outside of the Builder.
The transition wasn't completely frictionless. The initial export of our HR portal threw a warning about an old authorization scheme, and I spent an afternoon getting our SQLcl version updated to support the new commands. Those were one-time setup costs, not ongoing pain but they were real.
What I like most is that it meets you where you are. You can keep building in APEX Builder just like before, export to APEXlang when you need it in source control, and import back when you're done. Nothing forces you to change everything at once. The rewards real code review, proper CI/CD, AI tooling that actually works stack up quickly once you're in the loop.
If you've ever felt friction around code reviews, source control, or just wanting to understand your own app's structure more clearly give APEXlang a proper try. The setup is a couple of hours. The payoff starts immediately.
Comments
Post a Comment