Skip to content

Commit

Permalink
Merge pull request #56 from junjie-w/develop
Browse files Browse the repository at this point in the history
Merge Develop into Main
  • Loading branch information
junjie-w authored Dec 27, 2024
2 parents 76a579e + b794c4e commit 4ba602f
Show file tree
Hide file tree
Showing 37 changed files with 2,779 additions and 188 deletions.
155 changes: 96 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,110 @@
# Turborepo Design System starter with Changesets

This is an official React design system starter powered by Turborepo. Versioning and package publishing is handled by [Changesets](https://github.com/changesets/changesets) and fully automated with GitHub Actions.

## Using this example

Run the following command:

```sh
npx create-turbo@latest -e with-changesets
# API Client SDK Streamline Sample

A demonstration of streamlined API client SDK development, from API specification to client usage.

## Project Flow

1. API Development
2. Push Spec to SwaggerHub
3. Publish SDK to NPM
4. Use API Client Config in API Routes

## Project Structure

- Root
- apps
- ecom-app
- api (Route Handlers)
- lib (API Client Config)
- products-api
- users-api
- packages
- openapi-fetch-runtime
- workflows (GitHub Actions)

## Features

- 🔄 Automated API spec publishing
- 📦 Streamlined SDK generation
- 🛠 Centralized client configuration
- 🌟 Type-safe API interactions

## How It Works

### 1. API Development
- NestJS APIs with OpenAPI decorators
- Automated spec generation
- GitHub Actions workflow for spec publishing

### 2. SDK Generation
- OpenAPI Generator with customized templates
- Automated NPM package publishing
- Runtime package for shared utilities

### 3. Client Configuration
- Environment-based configuration
- Error handling and logging
- Client instance caching
- Request middleware

### 4. API Routes
- Type-safe API client usage
- Proxy route implementations
- Error handling and response mapping

## Usage

1. Push API Spec:
```bash
# Triggered via GitHub Actions
- GitHub workflow publishes spec
```

## What's inside?

This Turborepo includes the following:

### Apps and Packages

- `docs`: A placeholder documentation site powered by [Next.js](https://nextjs.org/)
- `@acme/core`: core React components
- `@acme/utils`: shared React utilities
- `@acme/tsconfig`: shared `tsconfig.json`s used throughout the monorepo
- `@acme/eslint-config`: ESLint preset

Each package and app is 100% [TypeScript](https://www.typescriptlang.org/).

### Utilities

This Turborepo has some additional tools already setup for you:

- [TypeScript](https://www.typescriptlang.org/) for static type checking
- [ESLint](https://eslint.org/) for code linting
- [Prettier](https://prettier.io) for code formatting

### Useful commands

- `yarn build` - Build all packages and the docs site
- `yarn dev` - Develop all packages and the docs site
- `yarn lint` - Lint all packages
- `yarn changeset` - Generate a changeset
- `yarn clean` - Clean up all `node_modules` and `dist` folders (runs each package's clean script)

### Changing the npm organization scope

The npm organization scope for this design system starter is `@acme`. To change this, it's a bit manual at the moment, but you'll need to do the following:
2. Generate SDK:
```bash
# Automated via GitHub Actions
- Pull spec from SwaggerHub
- Generate TypeScript client
- Publish to NPM
```

- Rename folders in `packages/*` to replace `acme` with your desired scope
- Search and replace `acme` with your desired scope
- Re-run `yarn install`
3. Use in Routes:
```typescript
import { getProductsApi } from '@/lib/api-client-config'

## Versioning and Publishing packages
export async function GET() {
const productsApi = getProductsApi()
const products = await productsApi.findAll()
return NextResponse.json(products)
}
```

Package publishing has been configured using [Changesets](https://github.com/changesets/changesets). Please review their [documentation](https://github.com/changesets/changesets#documentation) to familiarize yourself with the workflow.
## Links

This example comes with automated npm releases setup in a [GitHub Action](https://github.com/changesets/action). To get this working, you will need to create an `NPM_TOKEN` and `GITHUB_TOKEN` in your repository settings. You should also install the [Changesets bot](https://github.com/apps/changeset-bot) on your GitHub repository as well.
- [Products API Spec](https://app.swaggerhub.com/apis/junjie.wu/sample-products-api)
- [Users API Spec](https://app.swaggerhub.com/apis/junjie.wu/sample-users-api)
- [Products SDK Package](https://www.npmjs.com/package/@api-client-sdk-streamline-sample/products-api-client)
- [Users SDK Package](https://www.npmjs.com/package/@api-client-sdk-streamline-sample/users-api-client)

For more information about this automation, refer to the official [changesets documentation](https://github.com/changesets/changesets/blob/main/docs/automating-changesets.md)
## Development

### npm
```bash
# Install dependencies
npm install

If you want to publish package to the public npm registry and make them publicly available, this is already setup.
# Start development servers
npm dev

To publish packages to a private npm organization scope, **remove** the following from each of the `package.json`'s
# Run tests
npm test

```diff
- "publishConfig": {
- "access": "public"
- },
# Build all packages
npm build
```

### GitHub Package Registry
## Notes

While demonstrated in a monorepo, in a real-world scenario, each API might be a separate microservice, rather than being part of a monorepo. In this case, the following aspects of the demonstrated workflow would still be applicable:

See [Working with the npm registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry#publishing-a-package-using-publishconfig-in-the-packagejson-file)
- SDK Distribution: The `publish-sdk.yml` workflow can be used to distribute the generated SDKs via NPM, making them accessible to client applications.
- Reusable Client Configuration: The `api-client-config` approach can be adopted to provide a reusable client configuration, simplifying the integration process for client applications.
41 changes: 41 additions & 0 deletions apps/ecom-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
36 changes: 36 additions & 0 deletions apps/ecom-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## 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) - 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_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
35 changes: 35 additions & 0 deletions apps/ecom-app/app/api/products/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { NextResponse } from 'next/server'
import logger from '@/lib/api-client-config/logger'
import { handleApiError } from '@/lib/api-client-config/errors/handler'
import { getProductsApi } from '@/lib/api-client-config/api-factory'

export async function GET() {
try {
logger.info('Starting GET request to /api/products')
const productsApi = getProductsApi()
const products = await productsApi.productsControllerFindAll()
logger.debug({ products }, 'Products retrieved successfully')
return NextResponse.json(products)
} catch (error) {
return handleApiError(error)
}
}

export async function POST() {
try {
logger.info('Starting POST request to /api/products')
const productsApi = getProductsApi()
const newProduct = await productsApi.productsControllerCreate({
createProductDto: {
name: "Sample Product",
price: 29.99,
description: "A test product",
categories: ["test"]
}
})
logger.debug({ newProduct }, 'Product created successfully')
return NextResponse.json(newProduct)
} catch (error) {
return handleApiError(error)
}
}
34 changes: 34 additions & 0 deletions apps/ecom-app/app/api/users/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NextResponse } from 'next/server'
import logger from '@/lib/api-client-config/logger'
import { handleApiError } from '@/lib/api-client-config/errors/handler'
import { getUsersApi } from '@/lib/api-client-config/api-factory'

export async function GET() {
try {
logger.info('Starting GET request to /api/users')
const usersApi = getUsersApi()
const users = await usersApi.usersControllerFindAll()
logger.debug({ users }, 'Users retrieved successfully')
return NextResponse.json(users)
} catch (error) {
return handleApiError(error)
}
}

export async function POST() {
try {
logger.info('Starting POST request to /api/users')
const usersApi = getUsersApi()
const newUser = await usersApi.usersControllerCreate({
createUserDto: {
name: "Sample User",
email: "[email protected]",
phone: "000-000-0000"
}
})
logger.debug({ newUser }, 'User created successfully')
return NextResponse.json(newUser)
} catch (error) {
return handleApiError(error)
}
}
Binary file added apps/ecom-app/app/favicon.ico
Binary file not shown.
21 changes: 21 additions & 0 deletions apps/ecom-app/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--background: #ffffff;
--foreground: #171717;
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}

body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}
34 changes: 34 additions & 0 deletions apps/ecom-app/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
Loading

0 comments on commit 4ba602f

Please sign in to comment.