Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 30 additions & 72 deletions ENDPOINTS.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Builders App — API Endpoints

This app is **read-only**. It displays builders, projects, and teams that are
created and managed in the main Boundless app. Every read comes from the shared
This app is **read-only**. It displays builders and projects that are created and
managed in the main Boundless app. Every read comes from the shared
`boundless-nestjs` backend, so this app and boundless-platform stay consistent
(a project or user followed in the platform is read here from the same data).

Profiles, project pages, and organization pages live in the main Boundless app.
This app links out to them rather than rendering them. See
[Main app pages we link to](#main-app-pages-we-link-to).

The public read API was implemented by the backend team. This document mirrors
`boundless-nestjs/docs/builders-public-reads.md` and maps each endpoint to the
screen that uses it. If the two ever drift, the backend doc is the source of
Expand All @@ -30,8 +34,6 @@ truth.
## Status legend

- **Public**: implemented and public. Use directly.
- **Private-aware**: public, but returns `404` when the target profile is
private.

---

Expand All @@ -41,10 +43,8 @@ truth.
| --- | --- | --- |
| `GET /users/top-builders` | Top builders row. Card-ready. Accepts `limit`. | Public |
| `GET /projects/featured` | Featured projects row | Public |
| `GET /discover/landing` | Ecosystem feed / counts | Public |
| `GET /discover/recent-winners` | Recent winners highlight | Public |
| `GET /users/directory?limit=N` | Fallback source for a builders strip | Public |
| `GET /leaderboard` | Ranking + reputation (tier, score, stats). Not card-ready. | Public |
| `GET /users/directory?limit=1` | Stats strip count (`pagination.total`) | Public |
| `GET /projects?limit=1` | Stats strip count (`pagination.total`) | Public |

> **Top builders:** use `GET /users/top-builders`. It returns a flat array of
> card-ready builders in the standard `{ success, message, data, meta }`
Expand All @@ -57,13 +57,11 @@ truth.
>
> Map: `name` -> displayName, `image` -> avatarSrc, `location` -> location,
> `skills`/`followers`/`projects` straight across. Do **not** map `role` (it is
> the account role, e.g. `"user"`, not a professional title). `GET /leaderboard`
> is kept for ranking/reputation use only; it does **not** carry profile fields
> (role, location, followers, projects), which is why the landing top-builders
> row moved to `GET /users/top-builders`.
> the account role, e.g. `"user"`, not a professional title).
>
> A stats strip can derive counts from the `pagination.total` of
> `GET /users/directory`, `GET /projects`, and `GET /organizations`.
> The stats strip derives its Builders and Projects counts from the
> `pagination.total` of `GET /users/directory` and `GET /projects` (each called
> with `limit=1`).

---

Expand All @@ -76,70 +74,33 @@ truth.

> The directory is `GET /users/directory`, **not** `GET /users`. `GET /users` is
> Admin-only and is not used by this app.
>
> **Who is listed:** the directory returns only people who qualify as builders: a
> username, an avatar, at least one skill, not a staff account, and at least one
> piece of real work on Boundless (a published project, a hackathon entry, a
> bounty submission, a win, or a grant award). Signing up or creating an
> organization alone does not get someone listed. This app does not filter
> anything itself; the rule lives in the API.

---

## `/builders/[username]` — Builder Profile

| Endpoint | Purpose | Status |
| --- | --- | --- |
| `GET /users/:username` | Public profile. Embeds orgs, projects, stats, followStats. | Public |
| `GET /users/:username/stats` | Public profile stats + follow counts | Private-aware |
| `GET /users/:username/activity` | Contribution timeline (`limit`, `offset`) | Private-aware |
| `GET /users/:username/organizations` | Teams the builder belongs to, with role | Private-aware |
| `GET /users/:username/followers` · `/following` | Follower / following lists | Public |
| `GET /projects?creatorId=:userId` | The builder's projects | Public |

---

## `/projects` — Projects / Products Directory

| Endpoint | Purpose | Status |
| --- | --- | --- |
| `GET /projects` | Directory. Filters: `creatorId`, `organizationId`, `tags` (repeatable/CSV), `category`, `publicStatus`, `originType`, `featured`, `search`. Never returns `IDEA` drafts. | Public |
| `GET /projects/search` | Search public projects | Public |
| `GET /projects/filters` | Facets: `categories[]` + `tags[]` (with counts), `publicStatuses[]`, `originTypes[]` | Public |

---

## `/projects/[slug]` — Project Detail

| Endpoint | Purpose | Status |
| --- | --- | --- |
| `GET /projects/:slug` | Project header and body (PII-scrubbed) | Public |
| `GET /projects/:slug/members` | Contributor roster (from `ProjectMember`). Creator always included as `owner`. Drafts 404. | Public |
| `GET /follows/entity/project/:id/followers` | Project follower list. The generic `entity/:entityType/:entityId/followers` route stays authenticated. | Public |
| `GET /bounties?projectId=` · `GET /grants?projectId=` | Related opportunities | Public |

---

## `/teams` — Teams / Organizations Directory
## Main app pages we link to

| Endpoint | Purpose | Status |
| --- | --- | --- |
| `GET /organizations` | Public directory. `search`, `sortBy` (`createdAt\|name`), `sortOrder`, `page`, `limit`. Counts only, no member PII. | Public |
| `GET /organizations/search` | Search organizations | Public |
This app renders directories only. The detail pages for a builder, a project,
and an organization live in the main Boundless app, and cards link to them there:

---
| Entity | Main app page |
| --- | --- |
| Builder | `https://boundlessfi.xyz/profile/<username>` |
| Project | `https://boundlessfi.xyz/projects/<slug>` |
| Organization | `https://boundlessfi.xyz/organizations/<slug>` |
| Projects directory | `https://boundlessfi.xyz/projects` |

## `/teams/[slug]` — Team Detail

| Endpoint | Purpose | Status |
| --- | --- | --- |
| `GET /organizations/profile/:idOrSlug` | Team profile | Public |
| `GET /organizations/profile/:idOrSlug/funded` | Funded / supported projects | Public |
| `GET /organizations/:id/members` | Public roster (identity fields only, capped, with `total`) | Public |
| `GET /organizations/:id/stats` | Projects / hackathons / bounties / grants / participants / prize pool + followers | Public |
The origin comes from `NEXT_PUBLIC_BOUNDLESS_APP_URL` (fallback
`https://boundlessfi.xyz`). These routes are not built in this app.

---

## Cross-cutting

| Endpoint | Purpose | Status |
| --- | --- | --- |
| `GET /leaderboard` | Leaderboard widget | Public |
| `GET /follows/:entityType/:entityId/check` | "Am I following" state (only if this app has logged-in sessions) | Auth |

### Not built here (boundary)

`POST` / `DELETE /follows/:entityType/:entityId` and every other write stay in
Expand All @@ -154,9 +115,6 @@ From migration `20260725000000_builders_public_reads`:
- `BuilderStatus` enum (`AVAILABLE`, `OPEN_TO_WORK`, `BUSY`, `UNAVAILABLE`) and
indexed `UserProfile.country` / `UserProfile.status` back the directory filters
and facets.
- `ProjectMember` model (`projectId`, `userId`, `role`, `joinedAt`) is the
first-class replacement for the untyped `Project.teamMembers` JSON and backs
`GET /projects/:slug/members`.

## Shared data

Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

### Boundless Builders

The public showcase for the builders, projects, and teams shipping on Boundless.
The public showcase for the builders and projects shipping on Boundless.

</div>

Expand All @@ -28,10 +28,11 @@ The public showcase for the builders, projects, and teams shipping on Boundless.

**Boundless Builders** is a display-only sub-app of the Boundless platform, served at
`builders.boundlessfi.xyz`. It presents the people and work of the ecosystem:
builders, their projects and products, and development teams.
builders and their projects and products. Builder, project, and organization
detail pages live in the main Boundless app; this app links out to them.

This app **only displays** data. Creating, editing, and uploading builders,
projects, and teams all happen in the main Boundless app
This app **only displays** data. Creating, editing, and uploading builders and
projects all happen in the main Boundless app
([boundlessfi.xyz](https://boundlessfi.xyz)). This showcase reads that data and
renders it, so expect mostly read (`GET`) flows and no creation forms here.

Expand Down
2 changes: 1 addition & 1 deletion app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { siteConfig } from '@/lib/seo/site';

const ABOUT_DESCRIPTION =
'Learn why Boundless Builders exists and what you will find here: the builders, projects, and teams shipping on Stellar through Boundless.';
'Learn why Boundless Builders exists and what you will find here: the builders and projects shipping on Stellar through Boundless.';

export const metadata = buildPageMetadata({
title: 'About',
Expand Down
1 change: 0 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export const metadata: Metadata = {
'web3 projects',
'Boundless',
'developers',
'teams',
],
openGraph: {
...sharedOpenGraph,
Expand Down
5 changes: 2 additions & 3 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,8 @@ export default function Home() {
<span className='text-primary-500'>Stellar</span>
</h1>
<p className='mx-auto mt-6 max-w-2xl text-body-lg text-muted-foreground'>
Explore the projects, teams, and creators building across the
Boundless ecosystem. This app showcases the work. Creating and
uploading happens in the main Boundless app.
Explore the projects and creators building across the Boundless
ecosystem.
</p>
<div className='mt-10 flex flex-wrap items-center justify-center gap-4'>
<Button size='large'>Explore builders</Button>
Expand Down
3 changes: 0 additions & 3 deletions app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,3 @@ export default function sitemap(): MetadataRoute.Sitemap {
priority: route.priority,
}));
}

// TODO: When builder and project detail pages ship, extend this with generateSitemaps
// to include dynamic routes. See issue #380 for context.
2 changes: 1 addition & 1 deletion components/about/about-hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function AboutHero() {
</h1>
<p className='mt-4 text-lg leading-[1.2] tracking-[-0.48px] text-text-muted-brand lg:text-2xl'>
Boundless Builders is a living showcase of the developers, designers, and
teams shipping across the ecosystem. See who is building, follow their
makers shipping across the ecosystem. See who is building, follow their
work, and watch ideas become real.
</p>
</HeroSection>
Expand Down
7 changes: 1 addition & 6 deletions components/about/what-youll-find.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,10 @@ const ITEMS = [
description:
'The products being built, launched, and funded on Boundless, with the teams behind them.',
},
{
title: 'Teams',
description:
'The organizations turning ideas into shipped work, and the people who make them up.',
},
];

/**
* About-page section introducing the three things you can explore. Uses the
* About-page section introducing the two things you can explore. Uses the
* two-column feature-list layout (purple eyebrow + heading on the left, a
* dash-separated list on the right), mirroring the boundless-platform About
* mission section.
Expand Down
8 changes: 1 addition & 7 deletions components/landing/stats-strip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ const STATS = [
path: '/projects?limit=1',
queryKey: ['ecosystem-stats', 'projects'],
},
{
label: 'Teams',
path: '/organizations?limit=1',
queryKey: ['ecosystem-stats', 'teams'],
},
] as const;

function NumberSkeleton({ mobile = false }: { mobile?: boolean }) {
Expand All @@ -39,8 +34,7 @@ function NumberSkeleton({ mobile = false }: { mobile?: boolean }) {
/** Every list the strip counts documents the same `pagination` block. */
type CountedList =
| Schemas['BuildersDirectoryResponseDto']
| Schemas['PublicProjectListDto']
| Schemas['PublicOrganizationListDto'];
| Schemas['PublicProjectListDto'];

async function fetchTotal(path: string) {
const response = await apiFetch<CountedList>(path);
Expand Down
2 changes: 1 addition & 1 deletion lib/seo/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const siteConfig = {
'https://builders.boundlessfi.xyz'
),
description:
'Discover the builders, projects, and teams shipping on Stellar through Boundless. A public showcase of the people and products of the ecosystem.',
'Discover the builders and projects shipping on Stellar through Boundless. A public showcase of the people and products of the ecosystem.',
twitterHandle: '@boundless_fi',
locale: 'en_US',
/** The main Boundless app, where builders sign up and create. */
Expand Down
Loading