What if you could invent your own language?
\n\nImagine: you write code that reads like plain English. No extra brackets, no for and if, just clean, understandable instructions. Sounds like science fiction? Not at all. This is a DSL (Domain-Specific Language) — a domain-oriented language created for a specific task.
On PythonLib, we often talk about how to make code cleaner and more expressive. Today, we'll take an unexpected approach: we'll use JavaScript and turn it into a tool for creating your own mini-languages. Why? So your configurations stop being a "mess" and your business logic sounds like music.
\n\nReady to become a linguist in the world of programming? Let's go!
\n\nDSL: What is it and why do you need it?
\n\nA DSL (Domain-Specific Language) is a programming language (or its syntactic extension) created to solve problems in one specific domain. Unlike general-purpose languages (C++, Python, Java), a DSL doesn't try to cover everything. Its job is to be as expressive as possible within its niche.
\n\nReal-world examples:
\n- \n
- SQL — a language for working with databases. \n
- HTML — a markup language for web pages. \n
- Regex — a language for pattern matching. \n
- Makefile — a language for describing project builds. \n
Now imagine you can create such a convenient language for your own task: for describing business rules, for configuring bots, for building complex API queries. Creating a DSL in JavaScript is a superpower that makes code self-documenting and lowers the entry barrier for new developers.
\n\nInternal and External DSLs: Two Paths to One Goal
\n\nInternal DSL (Embedded DSL)
\nThis is an overlay on top of an existing language. You use JavaScript syntax but build "building blocks" of a new language from it. The most popular example is a Fluent API (method chaining).
\n\nPros: no need to write a parser, you use all the features of the host language (debugging, types, tools).
\nCons: limited by JavaScript syntax (no new operators).
External DSL
\nYou invent your own syntax from scratch, and then write a parser (or compiler) that turns it into JavaScript.
\n\nPros: complete syntactic freedom, you can create a very concise language.
\nCons: you need to write and maintain a parser, debugging is more difficult.
For most projects, DSL in JavaScript starts with the internal approach — it's simpler, and you can implement it today.
\n\nWriting Your First DSL: From Configuration to Poetry
\n\nLet's create an internal DSL for describing routes in a web application. Instead of writing:
\n\nconst routes = [\n { path: '/users', handler: usersHandler, method: 'GET' },\n { path: '/users/:id', handler: userById, method: 'GET' },\n { path: '/users', handler: createUser, method: 'POST' }\n];\n\nWe want to write like this:
\n\nconst router = new Router();\nrouter\n .get('/users', usersHandler)\n .get('/users/:id', userById)\n .post('/users', createUser);\n\nAlready better, right? But this is just the beginning. A real DSL should be even more expressive.
\n\nStep 1: Fluent API (Method Chaining)
\n\nThe simplest way to create a domain-specific language is to return this from each method.
class Router {\n constructor() {\n this.routes = [];\n }\n\n get(path, handler) {\n this.routes.push({ path, handler, method: 'GET' });\n return this; // key to chaining\n }\n\n post(path, handler) {\n this.route