├── src ├── api │ └── .gitkeep ├── extensions │ └── .gitkeep ├── admin │ ├── webpack.config.example.js │ └── app.example.js ├── index.js └── app.js ├── public ├── uploads │ └── .gitkeep └── robots.txt ├── database └── migrations │ └── .gitkeep ├── .eslintignore ├── favicon.ico ├── config ├── api.js ├── middlewares.js ├── admin.js ├── server.js └── database.js ├── .env.example ├── .editorconfig ├── README.md ├── .eslintrc ├── package.json ├── serverless.yml └── .gitignore /src/api/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/uploads/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/extensions/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .cache 2 | build 3 | **/node_modules/** 4 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/viktorfa/strapi-4-serverless-guide/HEAD/favicon.ico -------------------------------------------------------------------------------- /config/api.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rest: { 3 | defaultLimit: 25, 4 | maxLimit: 100, 5 | withCount: true, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # To prevent search engines from seeing the site altogether, uncomment the next two lines: 2 | # User-Agent: * 3 | # Disallow: / 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | HOST=0.0.0.0 2 | PORT=1337 3 | APP_KEYS= 4 | 5 | SERVER_URL=dev 6 | DATABASE_PASSWORD= 7 | DATABASE_USERNAME= 8 | DATABASE_NAME= 9 | DATABASE_HOST= 10 | -------------------------------------------------------------------------------- /config/middlewares.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 'strapi::errors', 3 | 'strapi::security', 4 | 'strapi::cors', 5 | 'strapi::poweredBy', 6 | 'strapi::logger', 7 | 'strapi::query', 8 | 'strapi::body', 9 | 'strapi::session', 10 | 'strapi::favicon', 11 | 'strapi::public', 12 | ]; 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{package.json,*.yml}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /src/admin/webpack.config.example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint-disable no-unused-vars */ 4 | module.exports = (config, webpack) => { 5 | // Note: we provide webpack above so you should not `require` it 6 | // Perform customizations to webpack config 7 | // Important: return the modified config 8 | return config; 9 | }; 10 | -------------------------------------------------------------------------------- /config/admin.js: -------------------------------------------------------------------------------- 1 | const getUrl = () => { 2 | if (process.argv.join(" ").includes("strapi develop")) { 3 | return "admin"; 4 | } else { 5 | return "dev/admin"; 6 | } 7 | }; 8 | 9 | module.exports = ({ env }) => ({ 10 | auth: { 11 | secret: env("ADMIN_JWT_SECRET"), 12 | }, 13 | apiToken: { 14 | salt: env("API_TOKEN_SALT"), 15 | }, 16 | url: getUrl(), 17 | autoOpen: false, 18 | }); 19 | -------------------------------------------------------------------------------- /config/server.js: -------------------------------------------------------------------------------- 1 | const getUrl = ({ env }) => { 2 | if (process.argv.join(" ").includes("strapi develop")) { 3 | return ""; 4 | } else if (env.bool("IS_OFFLINE") || env.bool("BUILD_FOR_OFFLINE")) { 5 | return "http://localhost:3000/dev"; 6 | } else { 7 | return env("SERVER_URL", "dev"); 8 | } 9 | }; 10 | 11 | module.exports = ({ env }) => ({ 12 | host: env("HOST", "0.0.0.0"), 13 | port: env.int("PORT", 1337), 14 | app: { 15 | keys: env.array("APP_KEYS"), 16 | }, 17 | url: getUrl({ env }), 18 | }); 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Lambda Serverless Strapi 4 Guide 2 | 3 | Check [this blog post](https://blog.vikfand.com/posts/serverless-strapi-4-guide/) for how to deploy Strapi 4 on Lambda with the Serverless framework. 4 | 5 | Remember to create a `.env` file if you clone this project. It is recommended to create your Strapi instance with `yarn create strapi-app serverless-strapi-4-guide --quickstart` as you will get the latest version with a recommeded configuration. See the [Strapi docs](https://docs.strapi.io/developer-docs/latest/getting-started/quick-start.html) for more info. -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | /** 5 | * An asynchronous register function that runs before 6 | * your application is initialized. 7 | * 8 | * This gives you an opportunity to extend code. 9 | */ 10 | register(/*{ strapi }*/) {}, 11 | 12 | /** 13 | * An asynchronous bootstrap function that runs before 14 | * your application gets started. 15 | * 16 | * This gives you an opportunity to set up your data model, 17 | * run jobs, or perform some special logic. 18 | */ 19 | bootstrap(/*{ strapi }*/) {}, 20 | }; 21 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "eslint:recommended", 4 | "env": { 5 | "commonjs": true, 6 | "es6": true, 7 | "node": true, 8 | "browser": false 9 | }, 10 | "parserOptions": { 11 | "ecmaFeatures": { 12 | "experimentalObjectRestSpread": true, 13 | "jsx": false 14 | }, 15 | "sourceType": "module" 16 | }, 17 | "globals": { 18 | "strapi": true 19 | }, 20 | "rules": { 21 | "indent": ["error", 2, { "SwitchCase": 1 }], 22 | "linebreak-style": ["error", "unix"], 23 | "no-console": 0, 24 | "quotes": ["error", "single"], 25 | "semi": ["error", "always"] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/admin/app.example.js: -------------------------------------------------------------------------------- 1 | export default { 2 | config: { 3 | locales: [ 4 | // 'ar', 5 | // 'fr', 6 | // 'cs', 7 | // 'de', 8 | // 'dk', 9 | // 'es', 10 | // 'he', 11 | // 'id', 12 | // 'it', 13 | // 'ja', 14 | // 'ko', 15 | // 'ms', 16 | // 'nl', 17 | // 'no', 18 | // 'pl', 19 | // 'pt-BR', 20 | // 'pt', 21 | // 'ru', 22 | // 'sk', 23 | // 'sv', 24 | // 'th', 25 | // 'tr', 26 | // 'uk', 27 | // 'vi', 28 | // 'zh-Hans', 29 | // 'zh', 30 | ], 31 | }, 32 | bootstrap(app) { 33 | console.log(app); 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /config/database.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ env }) => ({ 2 | connection: { 3 | client: "postgres", 4 | connection: { 5 | host: env("DATABASE_HOST", ""), 6 | port: env.int("DATABASE_PORT", 5432), 7 | database: env("DATABASE_NAME", ""), 8 | user: env("DATABASE_USERNAME", ""), 9 | password: env("DATABASE_PASSWORD", ""), 10 | schema: "public", 11 | ssl: { 12 | rejectUnauthorized: env.bool("DATABASE_SSL_SELF", false), // For self-signed certificates 13 | }, 14 | }, 15 | debug: false, 16 | // The connection pool can be adjusted, but these minimal limits seem to work well. 17 | pool: { 18 | min: 0, 19 | max: 1, 20 | idleTimeoutMillis: 500, 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-strapi-4-guide", 3 | "private": true, 4 | "version": "0.1.0", 5 | "description": "A Strapi application", 6 | "scripts": { 7 | "develop": "strapi develop", 8 | "start": "strapi start", 9 | "build": "strapi build", 10 | "strapi": "strapi" 11 | }, 12 | "dependencies": { 13 | "@strapi/plugin-i18n": "4.1.12", 14 | "@strapi/plugin-users-permissions": "4.1.12", 15 | "@strapi/strapi": "4.1.12", 16 | "better-sqlite3": "7.4.6", 17 | "pg": "^8.7.3", 18 | "serverless-http": "^3.0.1" 19 | }, 20 | "author": { 21 | "name": "A Strapi developer" 22 | }, 23 | "strapi": { 24 | "uuid": "7fd3b8a7-b4e8-40ac-b7b4-47d35ce5c562" 25 | }, 26 | "engines": { 27 | "node": ">=12.x.x <=16.x.x", 28 | "npm": ">=6.0.0" 29 | }, 30 | "license": "MIT", 31 | "devDependencies": { 32 | "serverless-offline": "^8.8.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const serverless = require("serverless-http"); 2 | const Strapi = require("@strapi/strapi/lib/index.js"); 3 | 4 | module.exports.hello = async (event, context) => { 5 | return { 6 | body: JSON.stringify({ message: "Hello from Serverless" }), 7 | headers: { "Content-Type": "application/json" }, 8 | }; 9 | }; 10 | 11 | const startStrapi = async (strapi) => { 12 | try { 13 | if (!strapi.isLoaded) { 14 | await strapi.load(); 15 | } 16 | await strapi.postListen(); 17 | strapi.server.mount(); 18 | return strapi; 19 | } catch (error) { 20 | return strapi.stopWithError(error); 21 | } 22 | }; 23 | 24 | module.exports.strapiHandler = async (event, context) => { 25 | let workingDir = process.cwd(); 26 | if (process.env.LAMBDA_TASK_ROOT && process.env.IS_OFFLINE !== "true") { 27 | workingDir = process.env.LAMBDA_TASK_ROOT; 28 | } 29 | if (!global.strapi) { 30 | console.info("Cold starting Strapi"); 31 | Strapi({ dir: workingDir }); 32 | } 33 | if (!global.strapi.isLoaded) { 34 | await startStrapi(global.strapi); 35 | } 36 | const handler = serverless(global.strapi.server.app); 37 | return handler(event, context); 38 | }; 39 | -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: strapi-4-serverless-guide 2 | 3 | frameworkVersion: "3" 4 | useDotenv: true 5 | 6 | provider: 7 | name: aws 8 | runtime: nodejs16.x 9 | region: eu-central-1 # NOTE: Update to your region 10 | profile: strapi-serverless-admin # NOTE: Update to your profile name as in ~/.aws/credentials 11 | logRetentionInDays: 1 12 | versionFunctions: false 13 | 14 | memorySize: 2048 15 | timeout: 30 16 | 17 | environment: 18 | APP_KEYS: ${env:APP_KEYS} 19 | API_TOKEN_SALT: ${env:API_TOKEN_SALT} 20 | ADMIN_JWT_SECRET: ${env:ADMIN_JWT_SECRET} 21 | JWT_SECRET: ${env:JWT_SECRET} 22 | 23 | SERVER_URL: ${env:SERVER_URL} 24 | DATABASE_PASSWORD: ${env:DATABASE_PASSWORD} 25 | DATABASE_USERNAME: ${env:DATABASE_USERNAME} 26 | DATABASE_NAME: ${env:DATABASE_NAME} 27 | DATABASE_HOST: ${env:DATABASE_HOST} 28 | 29 | package: 30 | excludeDevDependencies: true 31 | exclude: 32 | - "**" 33 | include: 34 | - "build/**" 35 | - "config/**" 36 | - "database/**" 37 | - "node_modules/**" 38 | - "public/**" 39 | - "src/**" 40 | - ".env.example" 41 | - "package.json" 42 | - "!node_modules/esbuild-linux-64/**" 43 | - "!node_modules/@babel/**" 44 | - "!node_modules/@types/**" 45 | - "!node_modules/webpack/**" 46 | - "!node_modules/@sentry/**" 47 | 48 | functions: 49 | api: 50 | handler: src/app.strapiHandler 51 | events: 52 | - http: 53 | path: / 54 | method: ANY 55 | cors: true 56 | - http: 57 | path: /{any+} 58 | method: ANY 59 | cors: true 60 | 61 | plugins: 62 | - serverless-offline 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ############################ 2 | # OS X 3 | ############################ 4 | 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | .Spotlight-V100 10 | .Trashes 11 | ._* 12 | 13 | 14 | ############################ 15 | # Linux 16 | ############################ 17 | 18 | *~ 19 | 20 | 21 | ############################ 22 | # Windows 23 | ############################ 24 | 25 | Thumbs.db 26 | ehthumbs.db 27 | Desktop.ini 28 | $RECYCLE.BIN/ 29 | *.cab 30 | *.msi 31 | *.msm 32 | *.msp 33 | 34 | 35 | ############################ 36 | # Packages 37 | ############################ 38 | 39 | *.7z 40 | *.csv 41 | *.dat 42 | *.dmg 43 | *.gz 44 | *.iso 45 | *.jar 46 | *.rar 47 | *.tar 48 | *.zip 49 | *.com 50 | *.class 51 | *.dll 52 | *.exe 53 | *.o 54 | *.seed 55 | *.so 56 | *.swo 57 | *.swp 58 | *.swn 59 | *.swm 60 | *.out 61 | *.pid 62 | 63 | 64 | ############################ 65 | # Logs and databases 66 | ############################ 67 | 68 | .tmp 69 | *.log 70 | *.sql 71 | *.sqlite 72 | *.sqlite3 73 | 74 | 75 | ############################ 76 | # Misc. 77 | ############################ 78 | 79 | *# 80 | ssl 81 | .idea 82 | nbproject 83 | public/uploads/* 84 | !public/uploads/.gitkeep 85 | 86 | ############################ 87 | # Node.js 88 | ############################ 89 | 90 | lib-cov 91 | lcov.info 92 | pids 93 | logs 94 | results 95 | node_modules 96 | .node_history 97 | 98 | ############################ 99 | # Tests 100 | ############################ 101 | 102 | testApp 103 | coverage 104 | 105 | ############################ 106 | # Strapi 107 | ############################ 108 | 109 | .env 110 | license.txt 111 | exports 112 | *.cache 113 | build 114 | .strapi-updater.json 115 | 116 | .serverless --------------------------------------------------------------------------------