diff options
Diffstat (limited to 'examples/with-docker/apps')
| -rw-r--r-- | examples/with-docker/apps/api/.eslintrc.js | 4 | ||||
| -rw-r--r-- | examples/with-docker/apps/api/Dockerfile | 48 | ||||
| -rw-r--r-- | examples/with-docker/apps/api/package.json | 42 | ||||
| -rw-r--r-- | examples/with-docker/apps/api/src/__tests__/server.test.ts | 22 | ||||
| -rw-r--r-- | examples/with-docker/apps/api/src/__tests__/tsconfig.json | 4 | ||||
| -rw-r--r-- | examples/with-docker/apps/api/src/index.ts | 9 | ||||
| -rw-r--r-- | examples/with-docker/apps/api/src/server.ts | 22 | ||||
| -rw-r--r-- | examples/with-docker/apps/api/tsconfig.json | 11 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/.eslintrc.js | 4 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/Dockerfile | 56 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/README.md | 30 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/next-env.d.ts | 5 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/next.config.js | 10 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/package.json | 26 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/public/.gitkeep | 7 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/src/pages/index.tsx | 65 | ||||
| -rw-r--r-- | examples/with-docker/apps/web/tsconfig.json | 5 |
17 files changed, 370 insertions, 0 deletions
diff --git a/examples/with-docker/apps/api/.eslintrc.js b/examples/with-docker/apps/api/.eslintrc.js new file mode 100644 index 0000000..2308ff9 --- /dev/null +++ b/examples/with-docker/apps/api/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["custom-server"], +}; diff --git a/examples/with-docker/apps/api/Dockerfile b/examples/with-docker/apps/api/Dockerfile new file mode 100644 index 0000000..1b8e878 --- /dev/null +++ b/examples/with-docker/apps/api/Dockerfile @@ -0,0 +1,48 @@ +# The web Dockerfile is copy-pasted into our main docs at /docs/handbook/deploying-with-docker. +# Make sure you update this Dockerfile, the Dockerfile in the web workspace and copy that over to Dockerfile in the docs. + +FROM node:alpine AS builder +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +RUN apk update +# Set working directory +WORKDIR /app +RUN yarn global add turbo +COPY . . +RUN turbo prune --scope=api --docker + +# Add lockfile and package.json's of isolated subworkspace +FROM node:alpine AS installer +RUN apk add --no-cache libc6-compat +RUN apk update +WORKDIR /app + +# First install dependencies (as they change less often) +COPY .gitignore .gitignore +COPY --from=builder /app/out/json/ . +COPY --from=builder /app/out/yarn.lock ./yarn.lock +RUN yarn install + +# Build the project and its dependencies +COPY --from=builder /app/out/full/ . +COPY turbo.json turbo.json + +# Uncomment and use build args to enable remote caching +# ARG TURBO_TEAM +# ENV TURBO_TEAM=$TURBO_TEAM + +# ARG TURBO_TOKEN +# ENV TURBO_TOKEN=$TURBO_TOKEN + +RUN yarn turbo run build --filter=api... + +FROM node:alpine AS runner +WORKDIR /app + +# Don't run production as root +RUN addgroup --system --gid 1001 expressjs +RUN adduser --system --uid 1001 expressjs +USER expressjs +COPY --from=installer /app . + +CMD node apps/api/dist/index.js diff --git a/examples/with-docker/apps/api/package.json b/examples/with-docker/apps/api/package.json new file mode 100644 index 0000000..abe79b4 --- /dev/null +++ b/examples/with-docker/apps/api/package.json @@ -0,0 +1,42 @@ +{ + "name": "api", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "tsc", + "clean": "rm -rf dist", + "dev": "nodemon --exec \"node -r esbuild-register ./src/index.ts\" -e .ts", + "lint": "tsc --noEmit && eslint \"src/**/*.ts*\"", + "start": "node -r esbuild-register ./src/index.ts", + "test": "jest --detectOpenHandles" + }, + "jest": { + "preset": "jest-presets/jest/node" + }, + "dependencies": { + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "express": "^4.17.1", + "logger": "*", + "morgan": "^1.10.0" + }, + "devDependencies": { + "@types/body-parser": "^1.19.0", + "@types/cors": "^2.8.10", + "@types/express": "^4.17.12", + "@types/jest": "^26.0.22", + "@types/morgan": "^1.9.2", + "@types/node": "^15.12.2", + "@types/supertest": "^2.0.11", + "esbuild": "^0.14.38", + "esbuild-register": "^3.3.2", + "eslint": "^7.32.0", + "eslint-config-custom-server": "*", + "jest": "^26.6.3", + "jest-presets": "*", + "nodemon": "^2.0.15", + "supertest": "^6.1.3", + "tsconfig": "*", + "typescript": "^4.5.3" + } +} diff --git a/examples/with-docker/apps/api/src/__tests__/server.test.ts b/examples/with-docker/apps/api/src/__tests__/server.test.ts new file mode 100644 index 0000000..c0c9806 --- /dev/null +++ b/examples/with-docker/apps/api/src/__tests__/server.test.ts @@ -0,0 +1,22 @@ +import supertest from "supertest"; +import { createServer } from "../server"; + +describe("server", () => { + it("health check returns 200", async () => { + await supertest(createServer()) + .get("/healthz") + .expect(200) + .then((res) => { + expect(res.body.ok).toBe(true); + }); + }); + + it("message endpoint says hello", async () => { + await supertest(createServer()) + .get("/message/jared") + .expect(200) + .then((res) => { + expect(res.body).toEqual({ message: "hello jared" }); + }); + }); +}); diff --git a/examples/with-docker/apps/api/src/__tests__/tsconfig.json b/examples/with-docker/apps/api/src/__tests__/tsconfig.json new file mode 100644 index 0000000..bf65be6 --- /dev/null +++ b/examples/with-docker/apps/api/src/__tests__/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": [".", "../."] +} diff --git a/examples/with-docker/apps/api/src/index.ts b/examples/with-docker/apps/api/src/index.ts new file mode 100644 index 0000000..fbb9fda --- /dev/null +++ b/examples/with-docker/apps/api/src/index.ts @@ -0,0 +1,9 @@ +import { createServer } from "./server"; +import { log } from "logger"; + +const port = process.env.PORT || 3001; +const server = createServer(); + +server.listen(port, () => { + log(`api running on ${port}`); +}); diff --git a/examples/with-docker/apps/api/src/server.ts b/examples/with-docker/apps/api/src/server.ts new file mode 100644 index 0000000..70ccf0b --- /dev/null +++ b/examples/with-docker/apps/api/src/server.ts @@ -0,0 +1,22 @@ +import { json, urlencoded } from "body-parser"; +import express from "express"; +import morgan from "morgan"; +import cors from "cors"; + +export const createServer = () => { + const app = express(); + app + .disable("x-powered-by") + .use(morgan("dev")) + .use(urlencoded({ extended: true })) + .use(json()) + .use(cors()) + .get("/message/:name", (req, res) => { + return res.json({ message: `hello ${req.params.name}` }); + }) + .get("/healthz", (req, res) => { + return res.json({ ok: true }); + }); + + return app; +}; diff --git a/examples/with-docker/apps/api/tsconfig.json b/examples/with-docker/apps/api/tsconfig.json new file mode 100644 index 0000000..74f3de4 --- /dev/null +++ b/examples/with-docker/apps/api/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "lib": ["ES2015"], + "module": "CommonJS", + "outDir": "./dist", + "rootDir": "./src" + }, + "exclude": ["node_modules"], + "extends": "tsconfig/base.json", + "include": ["src"] +} diff --git a/examples/with-docker/apps/web/.eslintrc.js b/examples/with-docker/apps/web/.eslintrc.js new file mode 100644 index 0000000..c8df607 --- /dev/null +++ b/examples/with-docker/apps/web/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["custom"], +}; diff --git a/examples/with-docker/apps/web/Dockerfile b/examples/with-docker/apps/web/Dockerfile new file mode 100644 index 0000000..6f7da76 --- /dev/null +++ b/examples/with-docker/apps/web/Dockerfile @@ -0,0 +1,56 @@ +# This Dockerfile is copy-pasted into our main docs at /docs/handbook/deploying-with-docker. +# Make sure you update both files! + +FROM node:alpine AS builder +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +RUN apk update +# Set working directory +WORKDIR /app +RUN yarn global add turbo +COPY . . +RUN turbo prune --scope=web --docker + +# Add lockfile and package.json's of isolated subworkspace +FROM node:alpine AS installer +RUN apk add --no-cache libc6-compat +RUN apk update +WORKDIR /app + +# First install the dependencies (as they change less often) +COPY .gitignore .gitignore +COPY --from=builder /app/out/json/ . +COPY --from=builder /app/out/yarn.lock ./yarn.lock +RUN yarn install + +# Build the project +COPY --from=builder /app/out/full/ . +COPY turbo.json turbo.json + +# Uncomment and use build args to enable remote caching +# ARG TURBO_TEAM +# ENV TURBO_TEAM=$TURBO_TEAM + +# ARG TURBO_TOKEN +# ENV TURBO_TOKEN=$TURBO_TOKEN + +RUN yarn turbo run build --filter=web... + +FROM node:alpine AS runner +WORKDIR /app + +# Don't run production as root +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +USER nextjs + +COPY --from=installer /app/apps/web/next.config.js . +COPY --from=installer /app/apps/web/package.json . + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./ +COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static +COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public + +CMD node apps/web/server.js diff --git a/examples/with-docker/apps/web/README.md b/examples/with-docker/apps/web/README.md new file mode 100644 index 0000000..4fae62a --- /dev/null +++ b/examples/with-docker/apps/web/README.md @@ -0,0 +1,30 @@ +## Getting Started + +First, run the development server: + +```bash +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/examples/with-docker/apps/web/next-env.d.ts b/examples/with-docker/apps/web/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/examples/with-docker/apps/web/next-env.d.ts @@ -0,0 +1,5 @@ +/// <reference types="next" /> +/// <reference types="next/image-types/global" /> + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/with-docker/apps/web/next.config.js b/examples/with-docker/apps/web/next.config.js new file mode 100644 index 0000000..ce29f15 --- /dev/null +++ b/examples/with-docker/apps/web/next.config.js @@ -0,0 +1,10 @@ +const path = require("path"); + +module.exports = { + reactStrictMode: true, + transpilePackages: ["ui"], + output: "standalone", + experimental: { + outputFileTracingRoot: path.join(__dirname, "../../"), + }, +}; diff --git a/examples/with-docker/apps/web/package.json b/examples/with-docker/apps/web/package.json new file mode 100644 index 0000000..82398e3 --- /dev/null +++ b/examples/with-docker/apps/web/package.json @@ -0,0 +1,26 @@ +{ + "name": "web", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "next build", + "dev": "next dev", + "lint": "next lint", + "start": "next start" + }, + "dependencies": { + "next": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "ui": "*" + }, + "devDependencies": { + "@types/node": "^17.0.12", + "@types/react": "^18.0.22", + "@types/react-dom": "^18.0.7", + "eslint": "7.32.0", + "eslint-config-custom": "*", + "tsconfig": "*", + "typescript": "^4.5.3" + } +} diff --git a/examples/with-docker/apps/web/public/.gitkeep b/examples/with-docker/apps/web/public/.gitkeep new file mode 100644 index 0000000..6ae665a --- /dev/null +++ b/examples/with-docker/apps/web/public/.gitkeep @@ -0,0 +1,7 @@ +This public directory can be used to store any static content, see: + + https://nextjs.org/docs/basic-features/static-file-serving + +To remove this directory, also remove the following line from the web `Dockerfile`: + + COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public diff --git a/examples/with-docker/apps/web/src/pages/index.tsx b/examples/with-docker/apps/web/src/pages/index.tsx new file mode 100644 index 0000000..6a82f97 --- /dev/null +++ b/examples/with-docker/apps/web/src/pages/index.tsx @@ -0,0 +1,65 @@ +import { useEffect, useState } from "react"; +import { Button } from "ui"; + +const API_HOST = process.env.NEXT_PUBLIC_API_HOST || "http://localhost:3001"; + +export default function Web() { + const [name, setName] = useState<string>(""); + const [response, setResponse] = useState<{ message: string } | null>(null); + const [error, setError] = useState<string | undefined>(); + + useEffect(() => { + setResponse(null); + setError(undefined); + }, [name]); + + const onChange = (e: React.ChangeEvent<HTMLInputElement>) => + setName(e.target.value); + + const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => { + e.preventDefault(); + + try { + const result = await fetch(`${API_HOST}/message/${name}`); + const response = await result.json(); + setResponse(response); + } catch (err) { + console.error(err); + setError("Unable to fetch response"); + } + }; + + const onReset = () => { + setName(""); + }; + + return ( + <div> + <h1>Web</h1> + <form onSubmit={onSubmit}> + <label htmlFor="name">Name </label> + <input + type="text" + name="name" + id="name" + value={name} + onChange={onChange} + ></input> + <Button type="submit">Submit</Button> + </form> + {error && ( + <div> + <h3>Error</h3> + <p>{error}</p> + </div> + )} + {response && ( + <div> + <h3>Greeting</h3> + <p>{response.message}</p> + <Button onClick={onReset}>Reset</Button> + </div> + )} + </div> + ); +} diff --git a/examples/with-docker/apps/web/tsconfig.json b/examples/with-docker/apps/web/tsconfig.json new file mode 100644 index 0000000..a355365 --- /dev/null +++ b/examples/with-docker/apps/web/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "tsconfig/nextjs.json", + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} |
