r/node • • 3h ago

Where do you host a small Express API + always-on worker + Postgres cheaply?

8 Upvotes

Small app: Express API, Postgres, and a worker that polls an outbox table every 5s for background jobs. Low traffic, but the worker can't sleep, so free tiers like Render free and Neon free don't work.

Options I'm weighing:

- AWS free plan for 6 months (then ~$25/month)

- Hetzner CX23 (~€6/month) with everything on one box

- Railway Hobby (~$5-8/month)

What would you pick, and what am I missing? Please suggest.


r/node • • 19h ago

Best practices for OpenAPI spec generation + validation with express.js?

5 Upvotes

I've recently started a TypeScript Express.js project and while I'm familiarizing myself, I'm struggling to know what the best way to support OpenAPI spec generation and how to streamline endpoint parameter validation.

For example, with Django & Django Rest Framework, you define your API endpoints, what they receive and return, and parameters are automatically validated and you can even generate OpenAPI specs from them with another library.

For Express, I'm seeing solutions like JSDoc, which require you to write specs in comments above function definitions, or express-openapi-validator, which require you to maintain the endpoints and spec separately, but performs the validation. I see tools for generating route code, but not vice versa.

Am I approaching this from the wrong angle? I really like having my source of truth lie in the actual code for an endpoint, with specs/validation coming from that definition, not the other way around. What packages/solutions do you recommend for this?


r/node • • 2h ago

Typed WebSockets for Node, with AsyncAPI docs and a client generated from them

2 Upvotes

Hi all, I want to share a WebSocket framework I built to make WebSocket apps typed and safe, keep the client and server schemas from drifting apart, and generate the docs for you.

Every WebSocket project I've worked on ended up looking the same. The server parses JSON and walks an if (data.action === 'chat') ... else if ... chain, checking payloads by hand. The client guesses the message shapes from a docs page that someone forgot to update. Rooms live in an in-memory Map, which works until you run a second instance. And when an HTTP route or a background job needs to push something to users, there's no clean way to reach the socket.

I first built it in Python (chanx, for Django and FastAPI), since that's my main backend language. Then my frontend needed a proper client, so I made chanx-js, which was client only at first. The Python side has been stable for a while now and some teams run it in production, so I ported the server to Node as well. You can now use it on Node, Express, Fastify, NestJS and others.

What it gives you:

  • One handler per message type, validated with Zod, Valibot, ArkType or any Standard Schema
  • AsyncAPI 3 docs generated from your code, served by the server
  • A typed client generated from those docs, so sending a wrong payload fails to compile
  • Groups and broadcasts across instances through Redis
  • chanx.broadcastEvent(...) to push to users from a route handler or a job
  • Works with ws, Express, Koa, Fastify, Hono, NestJS, Bun and Deno, and the client has React, Vue, Svelte and Solid bindings

A chat room looks like this:

and on the client, after npx @chanx-js/codegen http://localhost:8000/asyncapi.json:

Repo: https://github.com/chanx-dev/chanx-js

Docs: https://js.chanx.dev/

The Node server is still new, so if you run into any problem or have ideas to improve it, please open an issue or a discussion on the repo. Stars and PRs are welcome too.


r/node • • 10h ago

libtmux-ts 0.1 - tmux library for Node.js, TypeScript, bun

Thumbnail github.com
2 Upvotes

r/node • • 5h ago

I got tired of `process.env.X` being `string | undefined`, so I made a CLI that generates a zod-validated `env.ts` from your `.env`

0 Upvotes

Every project I start ends up with the same file: a zod schema for env vars, copied from the last project and slowly falling out of sync with .env. So I made type-envgen, which generates that file for you.

You write your .env as usual. Where you want more than the default, add a comment tag above the key:

```bash NODE_ENV=development

@type url

DATABASE_URL=postgres://localhost:5432/app

@min 1

@max 65535

PORT=3000

DEBUG=false

@type enum(debug, info, warn, error)

@default info

LOG_LEVEL=info

@min 32

JWT_SECRET=change-me-change-me-change-me-123

@optional

SENTRY_DSN= ```

Run npx type-envgen and you get this:

```ts import { z } from "zod"; import { validateEnv } from "type-envgen";

export const envSchema = z.object({ DATABASE_URL: z.url(), DEBUG: z.stringbool(), JWT_SECRET: z.string().min(32), LOG_LEVEL: z.enum(["debug","info","warn","error"]).default("info"), NODE_ENV: z.enum(["development","production","test"]), PORT: z.coerce.number().int().min(1).max(65535), SENTRY_DSN: z.string().optional(), });

export const env = validateEnv(envSchema, process.env); export type Env = z.infer<typeof envSchema>; ```

env.PORT is now a number and env.DEBUG is a boolean. If something is wrong, the app fails at startup with one readable list instead of crashing three files deep later on:

[type-envgen] Invalid environment variables: DATABASE_URL: missing JWT_SECRET: must be at least 32 characters NODE_ENV: must be one of: development, production, test PORT: must be at most 65535

The error messages never print the actual values, so secrets don't end up in your logs.

Other things it does:

  • No tags needed: untagged keys are typed from their value (numbers, booleans, URLs, emails, UUIDs, dates, IPs, even JSON objects).
  • Lots of types: about 40 @types map directly to zod 4 formats (jwt, hash, iban, e164, cidr…), plus checks like @pattern, @startswith and @nonempty, and transforms like @trim and @tolowercase.
  • Framework targets: --target vite | nextjs | astro | sveltekit | nuxt reads from the right place for each framework. It also warns if a key is missing the public prefix, so you don't ship a secret to the browser.
  • Safe to regenerate: your own code in env.ts is kept, and only the generated parts are replaced.
  • CI and dev: --check exits 1 in CI if the file is stale, and --watch regenerates while you develop.
  • Setup: npx type-envgen init asks a few questions and adds the npm scripts for you.

GitHub: https://github.com/OlaleyeFisayo/type-envgen

npm: type-envgen

It's MIT-licensed. I'd really like feedback, especially on what tags or targets you'd want next.