Quickstart
This walks you through a real deploy: a Database + Secret + WebApp on AWS. It's the sail-poc example shipped in the repo.
You'll need:
- Node 20+
- An AWS account + working credentials (
aws sts get-caller-identitysucceeds) - Docker running locally (for building the WebApp container)
1. Scaffold
mkdir orders && cd orders
npm init -y
npm install --save-dev @sprintsail/sdk @sprintsail/cli
mkdir src
2. Configure
sail.config.js
export default {
project: 'orders',
defaultTarget: 'aws:us-east-1',
};
(.js works for both config and infra. TypeScript also supported.)
3. Define infrastructure
infra.js
import { Project, Database, Secret, WebApp } from '@sprintsail/sdk';
export const app = new Project('orders');
// publiclyAccessible so the in-cluster migrate Job can reach it later if you migrate.
export const db = new Database(app, 'orders', {
engine: 'postgres',
version: '16',
publiclyAccessible: true,
});
export const apiKey = new Secret(app, 'api-key', {
initialValue: 'sk-orders-demo-key',
});
export const web = new WebApp(app, 'web', {
handler: './src/server.ts',
port: 8080,
cpu: '0.25',
memory: 512,
bindings: { db, apiKey },
});
4. Write the application
src/server.ts
import { createServer } from 'node:http';
import { db, apiKey } from '../infra.js';
const port = Number(process.env.PORT ?? 8080);
let schemaReady = false;
async function ensureSchema() {
if (schemaReady) return;
await db.query(
'CREATE TABLE IF NOT EXISTS orders (id serial primary key, customer text, amount integer)',
);
schemaReady = true;
}
createServer((req, res) => {
void (async () => {
if (req.url === '/healthz') {
res.writeHead(200); res.end('ok\n'); return;
}
await ensureSchema();
const key = await apiKey.value();
const rows = await db.query('SELECT id, customer, amount FROM orders ORDER BY id LIMIT 50');
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ keyPrefix: key.slice(0, 6), count: rows.length, rows }, null, 2));
})();
}).listen(port);
5. Deploy
npx sail deploy --yes
You'll see the plan, then the provisioning steps roll out. RDS takes ~5–10 minutes the first time; ECS Express takes ~2 minutes. When it finishes you'll have a public URL.
Plan for project "orders" → aws:us-east-1:
+ database orders
+ secret api-key
+ webapp web
[aws/database] creating orders-orders (postgres 16, db.t4g.micro). Status will be "available" in ~5-10 min.
[aws/secret] created orders/api-key
[aws/webapp] preparing orders-web
[aws/webapp] docker push 107537841227.dkr.ecr.us-east-1.amazonaws.com/sprintsail/orders-web:...
[aws/webapp] created orders-web
[aws/webapp] status: ACTIVE
Deploy complete.
Hit the URL — the WebApp will create the schema on first request and return an empty rows.
6. Seed some data
Find the RDS master credentials in Secrets Manager (the AWS provider names them <project>/orders-credentials):
aws secretsmanager get-secret-value --secret-id orders/orders-credentials \
--query SecretString --output text
Then psql and insert a few rows. (For seeding from your laptop you'll need to open the RDS security group to your IP on 5432.)
INSERT INTO orders (customer, amount) VALUES
('Ada Lovelace', 4200),
('Alan Turing', 1942),
('Grace Hopper', 1906);
Reload the WebApp URL — you'll see the rows in the JSON response.
7. Tear down
npx sail destroy --yes
This deletes the ECS service, the Lambda role, the Secret (7-day recovery), and the RDS instance. Bucket primitives (if any) refuse to delete if non-empty — empty them first.
Next
- Migrate this same app to a managed K8s runtime — see the AWS → Sprintsail Runtime tutorial.
- Learn the primitives — start with WebApp and Database.
- The CLI —
sailreference.