FAQ
General
What is Effortless AWS?
A TypeScript framework for AWS Lambda. You export handler functions — Effortless creates Lambda functions, DynamoDB tables, API Gateway routes, IAM roles, and everything else automatically. No YAML, no CloudFormation, no state files.
export const orders = defineTable({ schema: unsafeAs<Order>(),});This single export creates: a DynamoDB table, a typed client for .put() / .get(), and IAM permissions when used as a dependency. See Why Effortless? for the full story.
Do I need to learn CloudFormation or CDK?
No. Effortless doesn’t use CloudFormation at all. It makes direct AWS SDK calls to create and update resources. This means deploys take 5-10 seconds instead of minutes, and there are no stack limits, no drift, no rollback delays.
See Why Not CloudFormation? for a detailed comparison.
How is this different from SST?
SST is infrastructure as code — you write infrastructure definitions in sst.config.ts, then link them to your handler code. Effortless is infrastructure from code — you export a handler, and the infrastructure is created automatically.
Key differences: Effortless gives you typed clients from defineTable, deploys in 5-10s (vs ~30s for SST), and uses no state files. SST has a mature Console UI and broader community. See full comparison.
Is Effortless production-ready?
Effortless deploys standard AWS resources — Lambda, DynamoDB, API Gateway, SQS, CloudFront. These are the same services used by Netflix, Airbnb, and thousands of production workloads. The framework itself is early-stage, but the underlying infrastructure is battle-tested.
Does Effortless support multi-cloud?
No. AWS only. This is intentional — by focusing on one cloud, Effortless provides deep integration with AWS-native features like DynamoDB Streams, SQS FIFO ordering, and CloudFront edge caching. Multi-cloud frameworks sacrifice these capabilities for portability. See Why serverless?.
Deployment
How long does a deploy take?
Typically 5-10 seconds for a full deploy, 3-5 seconds for a code-only update. Effortless makes direct AWS SDK calls in parallel instead of going through CloudFormation.
| Operation | CloudFormation | Effortless |
|---|---|---|
| Create Lambda | 60-120s | 5-10s |
| Update Lambda code | 30-60s | 3-5s |
| Full redeploy | 5-10 min | 30-60s |
How do I deploy to different environments?
Use the --stage flag. Each stage gets fully isolated resources — separate tables, Lambdas, API Gateway.
eff deploy # default stage (dev)eff deploy --stage prod # productionNo shared state between stages. Each stage is completely independent.
Does Effortless use state files?
No. Effortless uses AWS resource tags as the source of truth. Every resource is tagged with the project name, stage, and handler name. To discover existing resources, it queries the Resource Groups Tagging API.
This means: no S3 backends, no lock files, no state drift, no “terraform import”. If the resource exists in AWS with the right tags, Effortless finds it.
Can I use Effortless in CI/CD?
Yes. eff deploy works in any environment with AWS credentials. For GitHub Actions:
- name: Deploy env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: eu-west-1 run: eff deploy --stage prodWhat happens if I stop using Effortless?
Your resources continue to work. Effortless creates standard AWS Lambda functions, DynamoDB tables, and API Gateway APIs. They don’t depend on Effortless at runtime. You can manage them with the AWS Console, CDK, or Terraform going forward.
AWS and Pricing
How much does it cost?
For most projects: $0-5/month. You pay only for AWS usage, not for Effortless.
AWS Free Tier (permanent, not trial):
- Lambda: 1M requests/month free
- DynamoDB: 25 GB storage + 25 read/write capacity units free
- Lambda Function URLs: no additional cost (included in Lambda pricing)
- SQS: 1M requests/month free
- CloudFront: 1 TB transfer/month free (first 12 months)
See Why serverless? — Why AWS for pricing details.
What AWS credentials do I need?
An IAM user or role with permissions to create Lambda functions, DynamoDB tables, IAM roles, and SQS queues. See Installation — AWS Credentials for setup options including ~/.aws/credentials, environment variables, and SSO.
What AWS region should I use?
The region closest to your users. Effortless supports any AWS region. Set it in effortless.config.ts:
export default defineConfig({ name: "my-app", region: "eu-west-1", // Ireland});Lambda and Performance
What about Lambda cold starts?
Node.js Lambda cold starts are typically 100-200ms. Warm invocations respond in single-digit milliseconds. Effortless bundles only the code each handler needs (via esbuild tree-shaking), which keeps cold starts minimal.
Heavy dependencies go into a shared Lambda Layer. This is handled automatically — you don’t configure anything.
Can I control Lambda memory and timeout?
Yes, in effortless.config.ts:
export default defineConfig({ name: "my-app", region: "eu-west-1", defaults: { memorySize: 512, // MB timeout: 30, // seconds },});You can also override per handler. See Configuration.
Can I use Lambda inside a VPC?
Yes. Configure VPC settings in effortless.config.ts:
export default defineConfig({ name: "my-app", region: "eu-west-1", vpc: { subnetIds: ["subnet-abc", "subnet-def"], securityGroupIds: ["sg-123"], },});Can I use npm packages in my handlers?
Yes. Effortless automatically bundles your code and node_modules with esbuild. Large dependencies are moved to a shared Lambda Layer (managed automatically with hash-based versioning). You don’t configure any of this.
DynamoDB
How do I create a DynamoDB table?
Export a defineTable call. That’s it.
import { defineTable, unsafeAs } from "effortless-aws";
type User = { id: string; email: string; name: string };
export const users = defineTable({ schema: unsafeAs<User>(),});This creates the table, a typed client (.put(), .get(), .delete()), and wires IAM permissions when used as deps. See Database guide.
Can I use a sort key?
Yes:
export const messages = defineTable({ schema: unsafeAs<Message>(),});How do I react to data changes?
Add onRecord to process each change, or onBatch for batch processing:
export const orders = defineTable({ schema: unsafeAs<Order>(), onRecord: async ({ record }) => { if (record.eventName === "INSERT") { console.log("New order:", record.new!.id); } },});This creates a DynamoDB Stream and a Lambda that processes changes in real time. See Database — Reacting to data changes.
Can I use a table from another handler?
Yes, via deps. This automatically wires IAM permissions:
import { orders } from "./db";
export const api = defineApi({ basePath: "/orders", deps: () => ({ orders }), get: { "/": async ({ deps }) => { // deps.orders has typed .get(), .put(), .delete() }, },});HTTP API
How do I validate request bodies?
Use the schema option with any validation library (e.g. Zod):
import { z } from "zod";
const userSchema = (input: unknown) => z.object({ email: z.string(), name: z.string() }).parse(input);
export const users = defineApi({ basePath: "/users", post: { "/": { schema: userSchema, handler: async ({ data }) => { // data.email and data.name are typed and validated }, }, },});Invalid requests get a 400 response before your handler runs. See HTTP API — Validating input.
How do I read path parameters?
They come from req.params:
export const users = defineApi({ basePath: "/users", get: { "/{id}": async ({ req }) => { const userId = req.params.id; }, },});Can I use secrets (API keys, tokens)?
Yes, via param() which reads from SSM Parameter Store:
import { param } from "effortless-aws";
export const checkout = defineApi({ basePath: "/checkout", params: { stripeKey: param("stripe/secret-key") }, post: async ({ params }) => { // params.stripeKey fetched once at cold start, cached after },});Create the secret with eff config set stripe/secret-key or manually via aws ssm put-parameter --name /my-app/dev/stripe/secret-key --value sk_... --type SecureString. If you forget, eff deploy warns about missing parameters. See HTTP API — Using secrets and CLI — config.
Why SSM Parameter Store and not Secrets Manager?
AWS has two main services for storing secrets: SSM Parameter Store and AWS Secrets Manager. Effortless uses Parameter Store because it covers the vast majority of serverless use cases with less cost and complexity.
| SSM Parameter Store | AWS Secrets Manager | |
|---|---|---|
| Cost | Free (standard tier) | $0.40/secret/month + API calls |
| Encryption | SecureString via KMS | Always encrypted via KMS |
| API | GetParameters (batch up to 10) | GetSecretValue (one at a time) |
| Rotation | Manual | Built-in automatic rotation |
| Use case | API keys, tokens, connection strings, config | Database credentials with automatic rotation |
Secrets Manager’s main advantage is automatic credential rotation — it can rotate RDS/Aurora passwords on a schedule without code changes. If you’re connecting to RDS via connection strings, that matters. For everything else (API keys, tokens, webhook secrets, feature flags), Parameter Store with SecureString type does the same job for free.
Other AWS configuration services and why they’re not used:
- AWS AppConfig — designed for feature flags and gradual rollouts with validation. Overkill for secrets and static config values.
- Environment variables — Effortless stores only SSM paths in env vars, never actual secret values. This prevents secrets from appearing in the Lambda console, deployment logs, or CloudFormation outputs.
If you need Secrets Manager for a specific use case (e.g., RDS credentials with rotation), you can call it directly from your handler code with the appropriate permissions:
export const data = defineApi({ basePath: "/data", permissions: ["secretsmanager:GetSecretValue"], get: async ({ req }) => { // Call Secrets Manager directly when you need rotation support },});Static Sites
Can I host a React/Vue/Astro site?
Yes, two options:
SSR framework (via CloudFront + Lambda Function URL + S3):
export const app = defineApp({ server: ".output/server", assets: ".output/public", build: "nuxt build",});Static site / SPA (via CloudFront + S3):
export const site = defineStaticSite({ dir: "dist", build: "npm run build", spa: true,});See Website guide for the full comparison.
Which hosting option should I choose?
defineApp | defineStaticSite | |
|---|---|---|
| Served from | CloudFront + Lambda Function URL + S3 | CloudFront + S3 |
| Server-side rendering | Yes | No |
| Global CDN | Yes | Yes |
| Best for | SSR frameworks (Nuxt, Astro SSR) | Static sites, SPAs, docs |
Queues
When should I use a queue vs DynamoDB streams?
Use DynamoDB streams (onRecord) when reacting to data changes — a new order triggers an email, a user update syncs to analytics.
Use SQS FIFO queues (defineFifoQueue) for task processing — sending emails, processing payments, generating reports. Queues give you retry logic, dead-letter queues, and backpressure handling.
See Queue — When to use queues vs streams.
Troubleshooting
My deploy says “credentials not found”
Effortless needs AWS credentials. Options:
~/.aws/credentialsfile (most common)AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYenvironment variables- AWS SSO via
aws sso login
See Installation — AWS Credentials.
My Lambda returns 502 Bad Gateway
Common causes:
- Handler threw an error — check CloudWatch Logs:
eff logs <handler-name> - Timeout — default is 30s, increase in config if needed
- Missing permissions — if your handler calls AWS services not managed by Effortless, add them to
permissionsin handler config
My DynamoDB table already exists
Effortless uses tags to discover resources. If you created a table manually with the same name, either:
- Delete it and let Effortless create it
- Add the correct tags:
effortless:project,effortless:stage,effortless:handler
Deploy is slow (>30s)
Usually means a large node_modules is being uploaded. Effortless uses Lambda Layers with hash-based caching — the first deploy with new dependencies is slower, subsequent deploys reuse the layer.