883e597d40
Epic 10 Story 10.2 — SDK для разработки backend-расширений Apollo Federation v2
subgraph'ов экосистемы кооператива.
Что внутри:
- ExtensionFederationModule.forRoot() — готовый GraphQL модуль с
ApolloFederationDriver + autoSchemaFile.federation: 2
- ExtensionAuthModule.forRoot({ secret }) — passport-jwt стратегия,
валидирует токен который Apollo Gateway forward'ит в Authorization-header.
- ExtensionJwtAuthGuard — guard для GraphQL resolver'ов
- SharedCooperator/SharedCooperative/SharedAccount — federation v2 stub'ы
на core-entity для cross-extension references (@key + @external)
- HealthController GET /_health — обязательный для orchestrator'а
- loadExtensionConfig() — fail-fast загрузчик env'ов
Templates:
- Dockerfile multi-stage (builder + runtime + wget healthcheck)
- docker-compose.snippet.yaml — шаблон service'а для orchestrator'а
Контракт фиксирован Epic 10 PRD (apps-catalog/docs/epics/E10-federation-runtime.md)
и Architecture v3 (blago req 8b-architecture-v3-runtime-federation).
3.2 KiB
3.2 KiB
@coopenomics/extension-sdk
SDK для разработки backend-расширений Apollo Federation v2 subgraph'ов экосистемы цифрового кооператива.
См. подробнее: apps-catalog/docs/epics/E10-federation-runtime.md.
Зачем
Расширение в платформе цифрового кооператива — это пара артефактов:
- frontend (
install.jsbundle, попадает в desktop через remote-loader) - backend (Nest-app, поднимается оркестратором как docker-контейнер и подключается к Apollo Gateway как subgraph)
Этот пакет задаёт контракт backend-части: какие модули обязательны (federation driver + JWT guard + healthcheck), как ссылаться на core-entity (Cooperator/Cooperative/Account), как читать env'ы.
Минимальный пример
// src/main.ts
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { loadExtensionConfig } from '@coopenomics/extension-sdk';
import { AppModule } from './app.module';
async function bootstrap() {
const cfg = loadExtensionConfig();
const app = await NestFactory.create(AppModule);
await app.listen(cfg.subgraphPort);
}
bootstrap();
// src/app.module.ts
import { Module } from '@nestjs/common';
import {
ExtensionFederationModule,
ExtensionAuthModule,
HealthController,
loadExtensionConfig,
} from '@coopenomics/extension-sdk';
import { ChatResolver } from './chat/chat.resolver';
const cfg = loadExtensionConfig();
@Module({
imports: [
ExtensionFederationModule.forRoot(),
ExtensionAuthModule.forRoot({ secret: cfg.jwtSecret }),
],
controllers: [HealthController],
providers: [ChatResolver],
})
export class AppModule {}
// src/chat/chat.resolver.ts
import { ResolveField, Resolver, Parent, Query, UseGuards } from '@nestjs/graphql';
import { SharedCooperator, ExtensionJwtAuthGuard } from '@coopenomics/extension-sdk';
import { ChatService } from './chat.service';
@Resolver(() => SharedCooperator)
export class ChatResolver {
constructor(private readonly chat: ChatService) {}
@ResolveField(() => [String])
@UseGuards(ExtensionJwtAuthGuard)
async chatThreads(@Parent() cooperator: SharedCooperator): Promise<string[]> {
return this.chat.listThreads(cooperator.username);
}
}
После build'а:
docker buildчерезtemplates/Dockerfiledocker push registry.coopenomics.world/<scope>/<name>:<version>npm publishinstall.js bundle- on-chain
apps::regpkgчерез voskhod operator - orchestrator подхватит после approve
ENV-переменные
| Переменная | Описание | Обязательная |
|---|---|---|
SUBGRAPH_PORT |
Порт subgraph'а внутри контейнера | ✓ |
JWT_SECRET |
Тот же что у core'а (выдаёт core, проверяет subgraph) | ✓ |
COOPNAME |
Account name кооператива-tenant'а | ✓ |
DATABASE_URL |
Postgres connection string (если расширение пишет в БД) | |
CORE_GRAPHQL_URL |
Endpoint core subgraph'а (для cross-extension queries) |