npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

startsimpli-api

v0.1.0

Published

Type-safe Django REST API client for StartSimpli apps

Readme

@startsimpli/api

Type-safe Django REST API client for StartSimpli Next.js applications.

Overview

This package provides a type-safe TypeScript client for the Django REST API backend (start-simpli-api). It handles authentication, error normalization, pagination, and provides endpoint wrappers for all Django models.

IMPORTANT: This is a Django REST API client - NO Prisma, NO database code. All data lives in the Django backend.

Installation

npm install @startsimpli/api

Usage

Basic Setup

import { createStartSimpliApi } from '@startsimpli/api';

const api = createStartSimpliApi({
  baseUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api/v1',
  getToken: async () => {
    // Get token from your auth provider
    const session = await getServerSession();
    return session?.accessToken || null;
  },
});

// Use API endpoints
const contacts = await api.contacts.list({ tier: 1 });
const orgs = await api.organizations.getByStage('seed');

Contacts API

// List contacts with filters
const contacts = await api.contacts.list(
  { tier: 1, has_linkedin: true }, // filters
  { page: 1, page_size: 20 },      // pagination
  { ordering: '-created_at' }       // sorting
);

// Get single contact
const contact = await api.contacts.get('contact-uuid');

// Create contact with assertions
const newContact = await api.contacts.create({
  name: 'John Doe',
  email: '[email protected]',
  title: 'Partner',
  write_tags: ['tier_1', 'focus:fintech'],
  write_metrics: { enrichment_score: 0.95 },
  write_profiles: { linkedin: 'https://linkedin.com/in/johndoe' },
});

// Update contact
const updated = await api.contacts.update('contact-uuid', {
  title: 'Managing Partner',
  write_tags: ['tier_1', 'vip'],
});

// Search contacts
const results = await api.contacts.search('john');

// Get contacts by firm
const firmContacts = await api.contacts.getByFirm('firm-uuid');

Organizations API

// List organizations with filters
const orgs = await api.organizations.list(
  {
    tier: 1,
    stage: 'seed',
    check_size_min_gte: 100000,
    check_size_max_lte: 1000000,
  },
  { page: 1, page_size: 20 },
  { ordering: '-aum' }
);

// Get single organization
const org = await api.organizations.get('org-uuid');

// Create organization with assertions
const newOrg = await api.organizations.create({
  name: 'Acme Ventures',
  domain: 'acmevc.com',
  location: 'San Francisco, CA',
  write_tags: ['tier_1', 'stage:seed', 'focus:fintech'],
  write_metrics: {
    check_size_min: 500000,
    check_size_max: 2000000,
    aum: 100000000,
  },
  write_profiles: {
    linkedin: 'https://linkedin.com/company/acme-ventures',
    website: 'https://acmevc.com',
  },
});

// Get by check size range
const firms = await api.organizations.getByCheckSizeRange(100000, 1000000);

Entities API (Low-level)

// Tags
const tags = await api.entities.listTags();

// Entity Tags
const entityTags = await api.entities.listEntityTags(
  { page: 1, page_size: 50 },
  { entity_id: 'some-uuid', category: 'focus' }
);

// Metrics
const metrics = await api.entities.listMetrics(
  undefined,
  { entity_id: 'some-uuid', type: 'financial' }
);

// Profiles
const profiles = await api.entities.listProfiles(
  undefined,
  { entity_id: 'some-uuid', type: 'professional' }
);

Using Direct Client

For custom endpoints not covered by wrappers:

import { createApiClient } from '@startsimpli/api';

const client = createApiClient({
  baseUrl: 'http://localhost:8000/api/v1',
  getToken: async () => getAccessToken(),
});

// Direct fetch calls
const data = await client.fetch.get('/custom-endpoint/', {
  params: { key: 'value' }
});

const created = await client.fetch.post('/custom-endpoint/', {
  name: 'test'
});

Next.js API Routes Middleware

With Auth

import { withAuth } from '@startsimpli/api/middleware';

export const GET = withAuth(async (request, context) => {
  const { userId, token, isAuthenticated } = context;

  if (!isAuthenticated) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  // Use token to call Django API
  return NextResponse.json({ userId, token });
});

With Validation

import { withValidation } from '@startsimpli/api/middleware';
import { z } from 'zod';

const createContactSchema = z.object({
  name: z.string().min(1),
  email: z.string().email().optional(),
  tier: z.number().int().min(1).max(3).optional(),
});

export const POST = withValidation(
  async (request, { body }) => {
    // body is typed and validated
    const contact = await createContact(body);
    return NextResponse.json(contact);
  },
  { body: createContactSchema }
);

With Error Handling

import { withErrorHandling } from '@startsimpli/api/middleware';

export const GET = withErrorHandling(async (request) => {
  // Errors are automatically caught and formatted
  const data = await somethingThatMightThrow();
  return NextResponse.json(data);
});

Composing Middleware

import { withAuth, withErrorHandling, withValidation } from '@startsimpli/api/middleware';
import { z } from 'zod';

const schema = z.object({ name: z.string() });

export const POST = withErrorHandling(
  withAuth(
    withValidation(
      async (request, { body }, authContext) => {
        // Fully typed, validated, and authenticated
        return NextResponse.json({ success: true });
      },
      { body: schema }
    )
  )
);

Error Handling

import {
  isApiException,
  isValidationError,
  isAuthError,
  isNotFoundError,
} from '@startsimpli/api';

try {
  const contact = await api.contacts.get('invalid-id');
} catch (error) {
  if (isValidationError(error)) {
    console.log('Validation errors:', error.errors);
  } else if (isAuthError(error)) {
    console.log('Auth error:', error.status);
  } else if (isNotFoundError(error)) {
    console.log('Contact not found');
  } else if (isApiException(error)) {
    console.log('API error:', error.message);
  }
}

TypeScript Types

All types are exported for use in your application:

import type {
  Contact,
  Organization,
  Entity,
  Tag,
  Metric,
  Profile,
  Attribute,
  PaginatedResponse,
  ApiError,
  CreateContactRequest,
  UpdateContactRequest,
  ContactFilters,
} from '@startsimpli/api';

Architecture

Django Integration

This package is designed to work with the Django REST API:

  • Base URL: http://localhost:8000/api/v1/ (configurable)
  • Auth: Bearer token from @startsimpli/auth
  • Pagination: Django REST Framework format (page, page_size)
  • Filtering: Django-filter query params (field__gte=100, field__in=1,2,3)

Entity System

Django uses a generic Entity model with assertions (Tags, Metrics, Profiles, Attributes):

// Entity with assertions
{
  id: 'uuid',
  entity_type: 'contact',

  // Canonical fields
  name: 'John Doe',
  email: '[email protected]',

  // Computed from assertions
  tier: 1,  // from tags
  linkedin: 'https://...', // from profiles
  enrichment_score: 0.95, // from metrics

  // Full assertions
  tags: [{ category: 'quality', name: 'tier_1', ... }],
  metrics: [{ type: 'quality', subtype: 'enrichment_score', value: 0.95 }],
  profiles: [{ type: 'professional', subtype: 'linkedin', identifier: '...' }],
}

Development

# Run tests
npm test

# Type check
npm run type-check

License

MIT