api

API Development Guide

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "api" with this command: npx skills add jvidalv/100cims/jvidalv-100cims-api

API Development Guide

You are working on the 100cims API (packages/api ), a Next.js + Elysia hybrid backend.

Key Files

File Purpose

src/api/routes/index.ts

Elysia app composition, error handling

src/app/api/[[...slugs]]/route.ts

Next.js catch-all for Elysia

src/db/schema.ts

Drizzle schema (source of truth)

src/db/index.ts

Database client

src/api/routes/@shared/jwt.ts

JWT middleware

src/api/routes/@shared/s3.ts

S3 upload utilities

src/api/lib/sheets.ts

Google Sheets logging

drizzle.config.ts

Database connection config

Architecture

Hybrid Stack

  • Next.js 15 (App Router) for web pages and runtime

  • Elysia 1.4 for API routes (mounted at /api/* )

  • Drizzle ORM with PostgreSQL

  • TypeBox for schema validation

Why This Hybrid?

Elysia provides excellent TypeScript inference, OpenAPI generation, and performance while Next.js handles the server runtime and potential web pages.

Directory Structure

  • /src/api/ : All Elysia API code

  • /routes/ : Route handlers (public, protected, @shared)

  • /schemas/ : TypeBox validation schemas

  • /lib/ : Utilities (sheets, dates, images, slug)

  • /src/db/ : Database schema and client

  • /src/app/ : Next.js pages and API catch-all route

Shared Utilities

File Purpose

src/api/lib/slug.ts

generateSlug()

  • URL-friendly slug generation

src/api/lib/images.ts

isBase64SizeValid()

  • Image size validation

src/api/lib/sheets.ts

Google Sheets logging utilities

src/api/lib/dates.ts

Date formatting utilities

Key Patterns

Route Organization

/api/routes/ ├── @shared/ # Middleware, JWT, S3, types ├── public/ # No auth required │ ├── mountains.route.ts │ ├── challenge.route.ts │ └── hiscores.route.ts ├── protected/ # JWT required │ ├── summit.route.ts │ ├── user.route.ts │ ├── plan.route.ts │ ├── mountains/ # Folder-based organization │ │ ├── index.ts │ │ ├── my-list.route.ts │ │ └── update.route.ts │ └── community-challenge/ │ ├── index.ts │ ├── create.route.ts │ ├── update.route.ts │ └── delete.route.ts └── index.ts # Compose all routes

Folder-based routes: Group related endpoints in folders with an index.ts that composes them with a prefix. Each endpoint gets its own file.

Creating Routes

import { Elysia } from 'elysia'; import { db } from '@/db'; import { userSchema } from '@/api/schemas';

export const userRoute = new Elysia({ prefix: '/user', tags: ['users'] }) .get('/:id', async ({ params }) => { const user = await db.query.user.findFirst({ where: (u, { eq }) => eq(u.id, params.id) }); return user; }, { detail: { summary: 'Get user by ID' }, params: userSchema.params, response: userSchema.response });

Protected Routes

import { jwt } from '@/api/routes/@shared/jwt'; import { store } from '@/api/routes/@shared/store';

export const summitRoute = new Elysia({ prefix: '/summit', tags: ['summits'] }) .use(jwt) .use(store) .derive(async ({ bearer, store }) => { const payload = await bearer(bearer); store.userId = payload.userId; }) .post('/', async ({ body, store }) => { // store.userId available from JWT const summit = await db.insert(summitTable).values({ userId: store.userId, mountainId: body.mountainId }); return summit; });

Database Queries

import { db } from '@/db'; import { user, summit, mountain } from '@/db/schema'; import { eq, desc } from 'drizzle-orm';

// Simple query const users = await db.select().from(user).where(eq(user.id, userId));

// Join query const summits = await db .select({ id: summit.id, mountainName: mountain.name, date: summit.createdAt }) .from(summit) .leftJoin(mountain, eq(summit.mountainId, mountain.id)) .where(eq(summit.userId, userId)) .orderBy(desc(summit.createdAt));

Schema Validation

import { t } from 'elysia';

export const summitSchema = { body: t.Object({ mountainId: t.String(), date: t.Optional(t.String()), image: t.Optional(t.String()) }), response: { 200: t.Object({ id: t.String(), mountainId: t.String(), userId: t.String() }) } };

Pagination Pattern

For paginated endpoints, use this pattern for backwards compatibility:

// Schema export const PaginatedItemsSchema = t.Object({ items: t.Array(ItemSchema), pagination: t.Object({ page: t.Number(), pageSize: t.Number(), totalItems: t.Number(), totalPages: t.Number(), hasMore: t.Boolean(), }), });

// Route handler - backwards compatible const isPaginated = query.page !== undefined || query.limit !== undefined;

if (isPaginated) { // Return paginated results with count query return { items: results, pagination: { page, pageSize, totalItems, totalPages, hasMore } }; }

// No pagination params = return ALL results (backwards compatible) return { items: results, pagination: { page: 1, pageSize: results.length, totalItems: results.length, totalPages: 1, hasMore: false } };

Key: Old clients without pagination params get all results. New clients can paginate.

Common Tasks

Add New Endpoint

  • Create schema in /api/schemas/

  • Create route file in /routes/public/ or /protected/

  • Import and use in /routes/index.ts

  • Mobile app: Run yarn generate-api-types

Database Migration

  • Update /src/db/schema.ts

  • Run yarn drizzle-kit push (pushes to DB)

  • Verify schema changes in database

Image Upload to S3

import { putImageOnS3 } from '@/api/routes/@shared/s3';

const key = ${process.env.APP_NAME}/user/avatar/${userId}.jpeg; await putImageOnS3(key, buffer);

Log to Google Sheets

import { addRowToSheets, ERRORS_SPREADSHEET } from '@/api/lib/sheets';

await addRowToSheets(ERRORS_SPREADSHEET, [ 'error_type', 'status_code', 'url', 'message' ]);

Environment Variables

  • DATABASE_URL : PostgreSQL connection string

  • AUTH_SECRET : JWT signing secret

  • AWS_* : S3 credentials (region, bucket, access keys)

  • SHEETS_* : Google service account credentials

  • APP_NAME : Application name (used in S3 paths)

See .env.example for complete list.

Swagger Documentation

Available at /api/swagger during development. Auto-generated from:

  • Route tags

  • TypeBox schemas

  • OpenAPI metadata in route definitions

Database Schema

See /src/db/schema.ts for full schema. Key tables:

  • user : OAuth accounts

  • mountain : Peak data (name, lat/lng, elevation, difficulty)

  • summit : User summit logs

  • plan : Group hiking plans

  • plan_attendee : Plan participants

  • plan_chat : Chat messages

  • challenge : Curated challenges

  • hiscores : Leaderboard

Error Handling

Global error handler in /routes/index.ts :

  • Logs all errors to Google Sheets

  • Returns appropriate HTTP status codes

  • Distinguishes ValidationError, ParseError, generic errors

Deployment

Vercel (configured in root vercel.json ):

  • Builds from packages/api

  • Environment variables set in Vercel dashboard

  • Automatic deployments on main branch

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

app

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

highlevel

Connect your AI assistant to GoHighLevel CRM via the official API v2. Manage contacts, conversations, calendars, pipelines, invoices, payments, workflows, and 30+ endpoint groups through natural language. Includes interactive setup wizard and 100+ pre-built, safe API commands. Python 3.6+ stdlib only — zero external dependencies.

Archived SourceRecently Updated
Coding

code-simplifier

No summary provided by upstream source.

Repository SourceNeeds Review
-21
api